/*
 * Copyright 2012 Subhabrata Ghosh <subho.ghosh at langene.net>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



package net.langene.common;

//~--- non-JDK imports --------------------------------------------------------

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipUtils;

//~--- JDK imports ------------------------------------------------------------

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

//~--- classes ----------------------------------------------------------------

/**
 * I/O Utilities for Directory delete, Un-compressing, etc.
 *
 * @author Subhabrata Ghosh <subho.ghosh at langene.net>
 */
public class IOUtils {

    /**
     * Uncompress an archive file.
     * - currently supports zip, gzip & tar archives.
     *
     * @param filename Archive filename.
     *
     * @return Directory to which the archive has been uncompressed.
     * @throws Exception
     */
    public static String uncompressedFile(String filename) throws Exception {
        if (filename.endsWith(".zip")) {
            return unzip(filename);
        } else if (GzipUtils.isCompressedFilename(filename)) {
            return gunzip(filename);
        } else if (filename.endsWith(".tar")) {
            return untar(filename);
        } else {
            return filename;
        }
    }

    /**
     * Unzip a Zip archive.
     *
     * @param filename Zip archive name.
     * @return Directory to which the archive has been uncompressed.
     * @throws Exception
     */
    public static String unzip(String filename) throws Exception {
        String	outdir = System.getProperty("java.io.tmpdir");
        File	afi    = new File(filename);

        outdir = outdir + "/" + afi.getName();

        File	di = new File(outdir);

        if (di.exists()) {
            if (di.isDirectory()) {
                deleteDirectory(di.getAbsolutePath());
            } else {
                di.delete();
            }
        }

        di.mkdirs();

        ZipFile				archive = new ZipFile(filename);
        Enumeration<ZipArchiveEntry>	entries = archive.getEntries();

        while (entries.hasMoreElements()) {
            ZipArchiveEntry	zae     = entries.nextElement();
            String		outfile = zae.getName();

            if (zae.isDirectory()) {
                String	dirname = di.getAbsolutePath() + "/" + outfile;
                File	ndi     = new File(dirname);

                if (!ndi.exists()) {
                    ndi.mkdirs();
                }

                continue;
            }

            File	nfi = new File(di.getAbsoluteFile() + "/" + outfile);
            File	ndi = nfi.getParentFile();

            if (!ndi.exists()) {
                ndi.mkdirs();
            }

            FileOutputStream	zos     = new FileOutputStream(nfi.getAbsoluteFile());
            InputStream		zis     = archive.getInputStream(zae);
            byte[]		content = new byte[2048];

            while (true) {
                int	size = zis.read(content);

                if (size <= 0) {
                    break;
                }

                zos.write(content, 0, size);
            }

            zos.close();
        }

        archive.close();

        return di.getAbsolutePath();
    }

    /**
     * Uncompress a Gzip archive.
     *
     * @param filename Gzip archive name.
     * @return Directory to which the archive has been uncompressed.
     * @throws Exception
     */
    public static String gunzip(String filename) throws Exception {
        String	outdir = System.getProperty("java.io.tmpdir");
        File	afi    = new File(filename);

        outdir = outdir + "/" + afi.getName();

        File	di = new File(outdir);

        if (di.exists()) {
            if (di.isDirectory()) {
                deleteDirectory(di.getAbsolutePath());
            } else {
                di.delete();
            }
        }

        di.mkdirs();

        String				gzoutfile = outdir + "/" + GzipUtils.getUncompressedFilename(afi.getName());
        FileInputStream			gfis      = new FileInputStream(afi.getAbsoluteFile());
        BufferedInputStream		gbis      = new BufferedInputStream(gfis);
        GzipCompressorInputStream	unzipper  = new GzipCompressorInputStream(gbis);
        FileOutputStream		gfos      = new FileOutputStream(gzoutfile);
        final byte[]			content   = new byte[2048];

        while (true) {
            int	size = unzipper.read(content);

            if (size <= 0) {
                break;
            }

            gfos.write(content, 0, size);
        }

        gfos.close();
        unzipper.close();

        if (gzoutfile.endsWith(".tar")) {
            return untar(di.getAbsolutePath() + "/content", new File(gzoutfile).getAbsolutePath());
        }

        return di.getAbsolutePath();
    }

    /**
     * Untar a Tar archive.
     *
     * @param filename Tar archive name.
     * @return Directory to which the archive has been uncompressed.
     * @throws Exception
     */
    public static String untar(String filename) throws Exception {
        String	outdir = System.getProperty("java.io.tmpdir");

        return untar(outdir, filename);
    }

    private static String untar(String directory, String filename) throws Exception {
        File	afi    = new File(filename);
        String	outdir = directory + "/" + afi.getName();
        File	di     = new File(outdir);

        if (di.exists()) {
            if (di.isDirectory()) {
                deleteDirectory(di.getAbsolutePath());
            } else {
                di.delete();
            }
        }

        di.mkdirs();

        FileInputStream		gfis    = new FileInputStream(afi.getAbsoluteFile());
        BufferedInputStream	gbis    = new BufferedInputStream(gfis);
        TarArchiveInputStream	archive = new TarArchiveInputStream(gbis);
        final byte[]		content = new byte[2048];

        while (true) {
            TarArchiveEntry	tae = archive.getNextTarEntry();

            if (tae == null) {
                break;
            }

            String	outfile = di.getAbsolutePath() + "/" + tae.getName();

            if (tae.isDirectory()) {
                File	ndi = new File(outfile);

                if (!ndi.exists()) {
                    ndi.mkdirs();
                }

                continue;
            }

            File	ndi = new File(outfile).getParentFile();

            if (!ndi.exists()) {
                ndi.mkdirs();
            }

            FileOutputStream	fos  = new FileOutputStream(outfile);
            long		size = tae.getSize();

            while (size > 0) {
                int	offset = archive.read(content);

                if (offset <= 0) {
                    break;
                }

                fos.write(content, 0, offset);
                size -= offset;
            }

            fos.close();
        }

        archive.close();

        return di.getAbsolutePath();
    }

    /**
     * Recursively delete the contents of the specified directory and the
     * directory itself.
     *
     * @param directory Directory to delete.
     *
     * @throws Exception
     */
    public static void deleteDirectory(String directory) throws Exception {
        File	di = new File(directory);

        if (!di.exists()) {
            return;
        }

        File[]	files = di.listFiles();

        if ((files != null) && (files.length > 0)) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file.getAbsolutePath());
                } else {
                    file.delete();
                }
            }
        }

        di.delete();
    }

    public static String findfirst(String directory, String filename) throws Exception {
        List<String>	paths = find(directory, filename);

        if (paths.size() > 0) {
            return paths.get(0);
        }

        return null;
    }

    public static List<String> find(String directory, String filename) throws Exception {
        List<String>	paths = new LinkedList<String>();

        find(directory, filename, paths);

        return paths;
    }

    private static void find(String directory, String filename, List<String> paths)
            throws Exception {
        File	di    = new File(directory);
        File[]	files = di.listFiles();

        if ((files != null) && (files.length > 0)) {
            for (File file : files) {
                if (file.isDirectory()) {
                    find(file.getAbsolutePath(), filename, paths);
                }

                if (file.getName().compareTo(filename) == 0) {
                    paths.add(file.getAbsolutePath());
                }
            }
        }
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
